#pragma once
#ifndef _KH_ENGINE_CLIENT_H
#define _KH_ENGINE_CLIENT_H
#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Units.h>
#include <Kharlia/Engine/Drawable.h>
#include <gl/glew.h>
#include <CEGUIInputEvent.h>
#include <CEGUIcolour.h>
#include <CEGUIDefaultResourceProvider.h>

namespace kharlia { namespace engine {

class ACamera;
class ARender;
class CEngine;
class CLevel;
class CClient;
class FTileLayer;
struct FTileset;
class FRenderView;
class FTexture;

enum EBufferType {
    BUFFER_TYPE_NONE,
    BUFFER_TYPE_VBO
};

struct FBufferInfo {
    bool Valid;
    EBufferType Type;

    FBufferInfo(): Valid(false), Type(BUFFER_TYPE_NONE) {}
};

class KH_ENGINE_DECLSPEC FResourceProvider: public ui::DefaultResourceProvider {
    shared_ptr<core::FLogger> mLogger;
public:

    FResourceProvider(shared_ptr<core::FLogger> l): mLogger(l) {}
    ~FResourceProvider() {}
    virtual void loadRawDataContainer(const ui::String& filename,
                                      ui::RawDataContainer& output,
                                      const ui::String& resourceGroup);
    virtual void unloadRawDataContainer(ui::RawDataContainer& data);
};

class KH_ENGINE_DECLSPEC CClient: public core::CObject {
    shared_ptr<core::FLogger> mLogger;
    shared_ptr<CEngine> mEngine;
    bool mInited;
    FVector2I mMousePos;
    shared_ptr<ACamera> mCamera;
    object mViewport;
    uint32 mWindowHandle;
    FSizeI mViewportSize;
    bool mIsLeftArrowPressed;
    bool mIsRightArrowPressed;
    bool mIsUpArrowPressed;
    bool mIsDownArrowPressed;
    bool mKeyPressTable[256];

    // GUI
    bool mUiInited;
    bool mUiFocused;
    ui::Renderer* mUiRenderer;
    ui::System* mUiSystem;
    FResourceProvider* mUiResourceProvider;
    ui::WindowManager* mUiWindowManager;
    ui::SchemeManager* mUiSchemeManager;
    ui::FontManager* mUiFontManager;
    std::vector<FRenderView*> mUiRenderViews;
    ui::Window* mUiDebugText;

    bool mRnInited;
    bool mRnRendering;
    bool mRnShowActorBounds;
    HDC mRnDeviceContext; // Device context handle from the window
    HGLRC mRnRenderingContext; // OpenGL rendering context handle
    ui::Texture* mRnErrorTexture;
    ui::Texture* mRnTilesetTexture;
    // Rendering implementation
    std::vector<FBufferInfo> mRnVertexBuffers;
    std::map<FSizeI, shared_array<GLfloat>> mRnTileVertexArrays;
    std::map<FSizeI, GLuint> mRnTileVertexBuffers;

    void UiInit();
    void UiFini();
    void RnInit();
    void RnFini();
    void RnConfig();

    void RnDrawScene(shared_ptr<ACamera> camera, const FRectF& vp);
    void RnDrawLevelActors(CLevel&, int, float, float, float, float);
    void RnDrawActor(ARender&);
    void RnDrawTileLayer(FTileLayer&);
    void RnDrawEditorBrush();
    void RnDrawEditorSelection();
    void RnCompileTileset(shared_ptr<FTileset>);

    shared_array<GLfloat> RnGetTileVertexArray(FSizeI size);
    GLuint                RnGetTileVertexBuffer(FSizeI size);
    void                  RnDeleteVertexBuffers();
    
    // Editor
    scoped_ptr<FRenderInfo> mEditorRenderInfo;

    KH_DECL_GC()

public:
    // Temporary public variables
    ui::Texture* TempActorTexture;
    shared_ptr<FTexture> TempActorTextureR;

    CClient(PyObject* self);
    virtual ~CClient();

    void Init(shared_ptr<CEngine> engine, object window);
    void Exit();
    virtual void Tick(float delta);
    FVector2F ConvertScreenToTileCoords(const FVector2I&);
    void SetMouseVisible(bool osstate, bool uistate);
    void InputMousePosition(int x, int y);
    void InputMouseButtonDown(ui::MouseButton b);
    void InputMouseButtonUp(ui::MouseButton b);
    void InputKeyDown(ui::Key::Scan keycode);
    void InputKeyUp(ui::Key::Scan keycode);
    inline bool IsKeyPressed(ui::Key::Scan keycode) const { return mKeyPressTable[uint8(keycode)]; }

    KH_DEF_GET(bool, Inited)
    KH_DEF_GET(const FVector2I&, MousePos)
    KH_DEF_GETSET(const shared_ptr<ACamera>&, Camera)
    KH_DEF_GET(const FSizeI&, ViewportSize)
    void SetViewportSize(const FSizeI& size);
    inline uint32 GetWindowHandle() { return mWindowHandle; }
    void SetWindowHandle(uint32 handle);

    // Renderer
    //shared_ptr<FTexture> RnCreateTexture(str name, str group, str filename);
    void RnRegisterVertexBuffers(int num, GLuint* ids);
    void RnUnregisterVertexBuffers(int num, GLuint* ids);

    KH_DEF_GETSET(bool, RnShowActorBounds)
    KH_DEF_GET(ui::Texture*, RnTilesetTexture)
    ui::Texture* GetRnErrorTexture();

    // Render Interface
    void RniDrawSprite(const FVector3F& pos, const ui::Texture* tex,
                       const FSizeF& size=FSizeF(), const ui::colour& color=ui::colour());

    // GUI
    bool OnTestButtonClicked(const ui::EventArgs& e);
    bool OnKeyDown(const ui::EventArgs& e);
    void OnRenderViewDraw(const FRenderView& rv, const ui::RenderingContext& ctxt);

    KH_DEF_GET(bool, UiFocused)
    KH_DEF_GET(ui::Renderer*, UiRenderer)
    KH_DEF_GET(FResourceProvider*, UiResourceProvider)

    friend void _InitClientClasses();
    friend void _InitClientBindings();
    friend void _ExitClientClasses();
};


}} // namespace kharlia::engine

KH_HAS_BACK_REFERENCE(kharlia::engine::CClient)

#endif // _KH_ENGINE_CLIENT_H